package com.bosicloud.cmb.ibp.edge.controller;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.bosicloud.cmb.ibp.account.api.AccountManagerClient;
import com.bosicloud.cmb.ibp.account.common.dto.LoginParamDTO;
import com.bosicloud.cmb.ibp.account.common.model.IbpUser;
import com.bosicloud.cmb.ibp.account.common.model.IbpUserApiKey;
import com.bosicloud.cmb.ibp.commom.dto.Pagination;
import com.bosicloud.cmb.ibp.commom.dto.UnionResult;
import com.bosicloud.cmb.ibp.commom.exception.ServiceException;
import com.bosicloud.cmb.ibp.commom.properties.EndpointProperties;
import com.bosicloud.cmb.ibp.edge.constant.SessionKeys;
import com.bosicloud.cmb.ibp.edge.constant.UrlDefine;
import com.bosicloud.cmb.ibp.edge.vo.LoginParamVO;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

/**
 * @author Jack Peng / ziliang.peng@bosicloud.com
 */
@SuppressWarnings("ALL")
@Api(value = "账号接口", description = "账号管理")
@RestController
public class AccountController {

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private AccountManagerClient accountManagerClient;

    @Autowired
    private Algorithm algorithm;

    @Autowired
    private EndpointProperties properties;


    @ApiOperation(value = "登录", response = UnionResult.class)
    @PostMapping(value = UrlDefine.LOGIN_API, consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    @HystrixCommand
    public Object login(@ApiParam(name = "parameters", value = "登录参数", required = true)
                        @RequestBody @Valid LoginParamVO parameters,
                        HttpServletRequest request) {

        HttpSession session = request.getSession(false);
        if (Objects.isNull(session)) {
            throw new ServiceException("未找到会话信息.", UnionResult.StatusEnum.FailToken.code());
        } else {
            String sessionVerificationCode = Optional.ofNullable(session.getAttribute(SessionKeys.VCODE))
                    .map(String::valueOf)
                    .orElse("");
            boolean checkEqualsVerificationCode = sessionVerificationCode.equalsIgnoreCase(parameters.getVerificationCode());
            if (checkEqualsVerificationCode) {
                IbpUser user = accountManagerClient.login(LoginParamDTO
                        .builder()
                        .username(parameters.getUsername())
                        .password(parameters.getPassword())
                        .build());
                if (Objects.nonNull(user)) {
                    session.setAttribute(SessionKeys.ACCOUNT, user);
                    return user;
                } else {
                    throw new ServiceException("账号或密码错误.", UnionResult.StatusEnum.FailLogin.code());
                }
            } else {
                throw new ServiceException("验证码错误.", UnionResult.StatusEnum.FailVCode.code());
            }
        }

    }

    @ApiOperation(value = "获取验证码,返回Base64编码,前端序从header返回头部获取X-Ibp-Identity-Token信息存储,以后每次请求都带上这个头部",
            response = UnionResult.class)
    @GetMapping(value = UrlDefine.VERIFICATION_CODE, produces = MediaType.APPLICATION_JSON_VALUE)
    @HystrixCommand
    public Object vcode(HttpServletRequest request, HttpServletResponse response) throws IOException {

        byte[] imageByte = null;
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

        // Save
        String createText = defaultKaptcha.createText();
        request.getSession().setAttribute(SessionKeys.VCODE, createText);

        BufferedImage challenge = defaultKaptcha.createImage(createText);
        ImageIO.write(challenge, "png", jpegOutputStream);

        imageByte = jpegOutputStream.toByteArray();

        return "data:image/png;base64," + Base64Utils.encodeToString(imageByte);

    }

    @ApiOperation(value = "获取下载Token", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.DOWNLOAD_KEY, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object downloadKey(HttpServletRequest request, HttpServletResponse response) throws IOException {

        IbpUser user = getUserFromSession(request);

        String token = JWT.create()
                .withSubject(JSON.toJSONString(user))
                .withExpiresAt(DateTime.now().plusSeconds(properties.getDownloadExpire()).toDate())
                .sign(algorithm);

        return token;
    }

    @ApiOperation(value = "下载资源", response = Void.class)
    @GetMapping(value = UrlDefine.DOWNLOAD_RES, produces = MediaType.APPLICATION_JSON_VALUE)
    public void downloadRes(@ApiParam(name = "token", value = "token", required = true)
                            @RequestParam(value = "token") String token,
                            @ApiParam(name = "env", value = "env", required = true)
                            @RequestParam(value = "env") String env,
                            HttpServletRequest request, HttpServletResponse response) throws IOException {

        try {

            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            DecodedJWT djwt = verifier.verify(token);

            IbpUser ibpUser = JSON.parseObject(djwt.getSubject(), IbpUser.class);

            String apiKey = accountManagerClient
                    .searchUserApiKeyListByUserId(ibpUser.getId())
                    .stream()
                    .filter(ibpUserApiKey -> Objects.equals(ibpUserApiKey.getEnv(), env))
                    .map(IbpUserApiKey::getAwsKey)
                    .findAny()
                    .orElse("");
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", String.format("attachment;filename=%s%s%s.csv",
                    env, "-key-", UUID.randomUUID().toString()));
            ServletOutputStream out = response.getOutputStream();
            out.write(apiKey.getBytes());
            out.flush();
            out.close();

        } catch (JWTVerificationException exception) {

            response.setContentType("text/plain; charset=utf-8");
            Writer out = response.getWriter();
            out.write("IBP: Invalid Token.");
            out.flush();
            out.close();
        }

    }

    @ApiOperation(value = "根据环境获取keyId", response = Pagination.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.KEY_ID + "/envs/{env}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object fetchKeyId(@ApiParam(name = "env", value = "env", required = true)
                             @PathVariable(value = "env") String env,
                             HttpServletRequest request) {
        return accountManagerClient
                .searchUserApiKeyListByUserId(getUserFromSession(request).getId())
                .stream()
                .filter(ibpUserApiKey -> Objects.equals(ibpUserApiKey.getEnv(), env))
                .map(IbpUserApiKey::getAwsKeyId)
                .findAny()
                .orElse("");
    }

    private IbpUser getUserFromSession(HttpServletRequest request) {
        Object obj = request.getSession().getAttribute(SessionKeys.ACCOUNT);
        if (Objects.isNull(obj)) {
            throw new ServiceException("未找到会话信息.", UnionResult.StatusEnum.FailToken.code());
        }
        return (IbpUser) obj;

    }
}
